using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;
using System.Web;
using Developex.DatabaseWrapper.Attributes;

namespace Developex.DatabaseWrapper
{
	/// <summary>
	/// Several helper functions used in DatabaseWrapper
	/// </summary>
	public class HelperFunctions
	{
		/// <summary>
		/// Auto detect DbType for IDataParameter by its value
		/// </summary>
		/// <param name="parameter"></param>
		/// <returns>returns true if auto-detection successed</returns>
		public static bool AutoDetectDbType(IDataParameter parameter)
		{
			if (parameter.Value == null || parameter.Value == DBNull.Value)
				return false;
			switch (parameter.Value.GetType().Name.ToLower())
			{
				case "int":
				case "int32":
					parameter.DbType = DbType.Int32;
					break;
				case "int16":
					parameter.DbType = DbType.Int16;
					break;
				case "int64":
					parameter.DbType = DbType.Int64;
					break;
				case "byte":
					parameter.DbType = DbType.Byte;
					break;
				case "string":
					parameter.DbType = DbType.String;
					break;
				case "double":
					parameter.DbType = DbType.Double;
					break;
				case "single":
					parameter.DbType = DbType.Single;
					break;
				case "decimal":
					parameter.DbType = DbType.Decimal;
					break;
				case "datetime":
					parameter.DbType = DbType.DateTime;
					break;
				case "byte[]":
					parameter.DbType = DbType.Binary;
					break;
				case "guid":
					parameter.DbType = DbType.Guid;
					break;
				default:
					return false;
			}
			return true;
		}

		/// <summary>
		/// Encode text and replace \n with line breaks
		/// </summary>
		/// <param name="text"></param>
		/// <returns></returns>
		public static string HtmlEncodeWithLineBreaks(string text)
		{
			if (text == null)
				return null;
			return HttpUtility.HtmlEncode(text).Replace("\r\n", "\n").Replace("\n", "<br />").Replace("\r", "<br />");
		}

		///<summary>
		///</summary>
		///<param name="qb"></param>
		///<param name="fields"></param>
		///<param name="ignoreHeavyFields"></param>
		public static void AddToQueryBuilder(QueryBuilder qb, ICollection<IField> fields, bool ignoreHeavyFields)
		{
			foreach (IField field in fields)
			{
				if (ignoreHeavyFields && (field.Flags & FieldFlags.HeavyField) != 0)
					continue;
				field.AddToSelectQuery(qb);
			}
		}

		///<summary>
		/// Add fields to query builder
		///</summary>
		///<param name="qb"></param>
		///<param name="fields"></param>
		public static void AddToQueryBuilder(QueryBuilder qb, ICollection<IField> fields)
		{
			AddToQueryBuilder(qb, fields, true);
		}

		///<summary>
		/// Add fields to query builder
		///</summary>
		///<param name="qb"></param>
		///<param name="fields"></param>
		///<typeparam name="T"></typeparam>
		public static void AddToQueryBuilder<T>(QueryBuilder qb, ICollection<T> fields) where T : IFieldBase
		{
			foreach (IFieldBase field in fields)
			{
				if (field == null) continue;
				field.AddToSelectQuery(qb);
			}
		}

		///<summary>
		///</summary>
		///<param name="field"></param>
		///<returns></returns>
		internal static string GetSearchFormatString(IField field)
		{
			if (field.GetDbType() == DbType.DateTime || field.GetDbType() == DbType.DateTime2)
				return "convert(varchar,{0},3) like {1}";
			return "{0} like {1}";
		}

		#region Convert IDataReader Results

		///<summary>
		/// Convert reader results to scalar list
		///</summary>
		///<param name="reader"></param>
		///<typeparam name="T"></typeparam>
		///<returns></returns>
		public static List<T> ReadAsList<T>(IDataReader reader)
		{
			bool valueType = typeof (T).IsValueType;
			List<T> values = new List<T>();
			while (reader.Read())
			{
				if (reader[0] == DBNull.Value)
				{
					if (!valueType)
						values.Add(default(T));
				}
				else values.Add((T)reader[0]);
			}
			return values;
		}

		///<summary>
		/// Convert reader results to scalar list without nulls
		///</summary>
		///<param name="reader"></param>
		///<typeparam name="T"></typeparam>
		///<returns></returns>
		/// <remarks>for value types you can use ReadAsList function (behaviour is the same)</remarks>
		public static List<T> ReadAsListWithoutNulls<T>(IDataReader reader)
		{
			List<T> values = new List<T>();
			while (reader.Read())
			{
				if (reader[0] != DBNull.Value)
					values.Add((T)reader[0]);
			}
			return values;
		}

		///<summary>
		/// Convert reader results to dictionary
		///</summary>
		///<param name="reader"></param>
		///<typeparam name="TKey"></typeparam>
		///<typeparam name="TValue"></typeparam>
		///<returns></returns>
		/// <remarks>if TKey or TValue is value type and according value in results is DBNull then row of results will be skipped</remarks>
		public static Dictionary<TKey, TValue> ReadAsDictionary<TKey, TValue>(IDataReader reader)
		{
			bool valueTypeTKey = typeof(TKey).IsValueType;
			bool valueTypeTValue = typeof(TValue).IsValueType;
			Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();
			while (reader.Read())
			{
				if (valueTypeTKey && reader[0] == DBNull.Value)
					continue;
				if (valueTypeTValue && reader[1] == DBNull.Value)
					continue;
				dict.Add(reader[0] == DBNull.Value ? default(TKey) : (TKey)reader[0],
					reader[1] == DBNull.Value ? default(TValue) : (TValue)reader[1]);
			}
			return dict;
		}

		#endregion
	}
}
